#pragma once
#include <iostream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/stat.h>

#include <sys/socket.h>
#include <sys/types.h>
#include <arpa/inet.h>
#include <netinet/in.h>


const int back_log = 10;

class Socket
{
private:
    int _sockfd;

public:
    Socket(){}
    ~Socket(){}

public:
    void Create();

    void Bind(uint16_t port);

    void Listen();

    int Accept(std::string *client_ip, uint16_t *client_port);

    bool Connect(const std::string& server_ip, const uint16_t& server_port);

    void Close();

    int GetSockfd(){ return _sockfd;}
};


////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void Socket::Create()
{
    //创建套接字
    _sockfd = socket(AF_INET, SOCK_STREAM, 0);
    if(_sockfd < 0)
    {
        std::cout << "socket create fail" << std::endl;
        exit(1);
    }
    
    int opt = 1;
    setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR | SO_REUSEPORT, &opt, sizeof(opt));
}

void Socket::Bind(uint16_t port)
{
    //1.填充服务器信息
    struct sockaddr_in server;
    memset(&server, 0, sizeof(server));
    server.sin_family = AF_INET;
    server.sin_port = htons(port);
    server.sin_addr.s_addr = INADDR_ANY;

    //2.开始bind
    if(bind(_sockfd, (struct sockaddr*)&server, sizeof(server)) < 0)
    {
        std::cout << "socket bind fail" << std::endl;
        exit(2);
    }

}

void Socket::Listen()
{
    //监听
    if(listen(_sockfd, back_log) < 0)
    {
        std::cout << "listen fail" << std::endl;
        exit(3);
    }
}

int Socket::Accept(std::string *client_ip, uint16_t *client_port)
{
    //获取客户端信息 + 拿到连接
    struct sockaddr_in client;
    socklen_t len = sizeof(client);
    int newsocket = accept(_sockfd, (struct sockaddr*)&client, &len);
    if(newsocket < 0)
    {

        std::cout << "accept fail" << std::endl;
        return -1;
    }

    char ipstr[64];
    inet_ntop(AF_INET, &client.sin_addr, ipstr, sizeof(client));
    *client_ip = ipstr;
    *client_port = ntohs(client.sin_port);

    return newsocket;
}

bool Socket::Connect(const std::string& server_ip, const uint16_t& server_port)
{
    //填充服务端信息
    struct sockaddr_in server;
    server.sin_family = AF_INET;
    server.sin_port = htons(server_port);
    inet_pton(AF_INET, server_ip.c_str(), &server.sin_addr);
    int n = connect(_sockfd, (struct sockaddr*)&server, sizeof(server));

    if(n == -1)return false;

    return true;
}


void Socket::Close()
{
    close(_sockfd);
}
